package com.zhuiyun.project.websocket;


import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zhuiyun.project.api.check.model.CheckModel;
import com.zhuiyun.project.api.check.service.CheckService;
import com.zhuiyun.project.api.friend.service.FriendService;
import com.zhuiyun.project.api.group.service.GroupService;
import com.zhuiyun.project.api.groupmessage.model.GroupMessageModel;
import com.zhuiyun.project.api.groupmessage.service.GroupMessageService;
import com.zhuiyun.project.api.joingroup.service.JoinGroupService;
import com.zhuiyun.project.api.message.config.MessageType;
import com.zhuiyun.project.api.message.service.MessageService;
import com.zhuiyun.project.api.personmessage.model.PersonMessageModel;
import com.zhuiyun.project.api.personmessage.service.PersonMessageService;
import com.zhuiyun.project.util.SpringContextUtil;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import springfox.documentation.spring.web.json.Json;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * type :sys后台，app前台
 */
@ServerEndpoint("/dchat/{id}/{type}")
@Component
public class SocketServer {

    ///用来保存,用户的session key是每个客户的id和用户类型(sys_1,app_1)， value是每个客户端的会话
    public static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
    ///用来保存,群聊的session key是群聊， 为同一个房间的用户集合
    public static Map<String, Set<Session>> groupMap = new ConcurrentHashMap<>();

    /**
     * @param session
     * @param id
     * @param type
     * @Author kdj
     * @Description 前端发送请求建立websocket连接，就会执行@OnOpen方法
     * @Date 2023/6/26 11:30
     * @Return void
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("id") String id, @PathParam("type") String type) {
        System.out.println(id);
        // 日志
        System.out.println(type + "用户：" + id + "上线了\t" + MyDateUtil.simpleDateFormat.format(new Date()));

        String key = type + "_" + id;
        Session cacheSession = sessionMap.get(key);

        if (cacheSession == null) {
            // 用户第一次登录，缓存用户的 session
            sessionMap.put(key, session);
            System.out.println("User session cached: " + session.getId());
        } else if (!cacheSession.getId().equals(session.getId())) {
            // 用户在其他设备上登录，挤下线
            sessionMap.put(key, cacheSession);
            // 发送消息给其他用户，提示被踢下线
            //先来系统用户
            if (type.equals("sys")) {
                this.sendMessageById(key, JSONUtil.toJsonStr(new Message<>(null, "kicked", null)));
            }

            System.out.println("User kicked out: " + cacheSession.getId());
        }
        sessionMap.put(key, session);
        JoinGroupService joinGroupService = SpringContextUtil.getBean(JoinGroupService.class);
        List<Integer> joinedGroups = joinGroupService.queryGroupsByUserId(Integer.parseInt(id),
                type.equals(MessageType.socketSys) ? MessageType.socketSysNum : MessageType.socketAppNum);

        for (Integer groupId : joinedGroups) {
            groupMap.computeIfAbsent(groupId.toString(), k -> new HashSet<>()).add(session);
        }

        System.out.println("当前在线人数：" + sessionMap.size());
        System.out.println("在线的人：" + sessionMap.keySet());
        System.out.println("在线的群聊：" + groupMap.keySet());
    }


    /**
     * @param session
     * @param id
     * @Author kdj
     * @Description 前端关闭页面或者主动关闭websocket链接，都会执行@OnClose
     * @Date 2023/6/26 11:30
     * @Return void
     */
    @OnClose
    public void onClose(Session session, @PathParam("id") String id, @PathParam("type") String type) {
        String key = type + "_" + id;
        Session cacheSession = sessionMap.get(key);

        if (cacheSession == null) {
            System.out.println("用户：" + key + "正常下线\t" + MyDateUtil.simpleDateFormat.format(new Date()) + "session为空");
        } else {
            try {
                cacheSession.close();
                sessionMap.remove(key);

                JoinGroupService joinGroupService = SpringContextUtil.getBean(JoinGroupService.class);
                //查询该用户的所有群聊id
                List<Integer> joinedGroups = joinGroupService.queryGroupsByUserId(Integer.parseInt(id),
                        type.equals(MessageType.socketSys) ? MessageType.socketSysNum : MessageType.socketAppNum);
                //遍历
                for (Integer groupId : joinedGroups) {
                    Set<Session> room = groupMap.get(groupId.toString());
                    //从中把自己的session删掉
                    if (room != null) {
                        room.remove(session);
                        //如果set集合空了删掉该set
                        if (room.isEmpty()) {
                            groupMap.remove(groupId.toString());
                        } else {
                            groupMap.put(groupId.toString(), room);
                        }
                    }
                }
            } catch (IOException e) {
                System.out.println("关闭连接出现错误");
            }

            this.sendAllMessage(JSONUtil.toJsonStr(new Message<>(200, "online-num", sessionMap.size())));
            System.out.println("用户：" + key + "正常下线\t" + MyDateUtil.simpleDateFormat.format(new Date()));
        }

        System.out.println("当前在线人数：" + sessionMap.size());
        System.out.println(sessionMap.keySet() + "\n");
    }

    @OnError
    public void onError(Session session, Throwable error, @PathParam("id") String id, @PathParam("type") String type) {
        String key = type + "_" + id;
        Session cacheSession = sessionMap.get(key);
        if (ObjectUtils.isEmpty(cacheSession)) {
            System.out.println("用户：" + key + "异常下线\t" + MyDateUtil.simpleDateFormat.format(new Date()) + error + "session为空");
            System.out.println("当前在线人数：" + sessionMap.size());
            System.out.println(sessionMap.keySet() + "\n");
            return;
        }
        try {
            cacheSession.close();
            sessionMap.remove(key, cacheSession);
            JoinGroupService joinGroupService = SpringContextUtil.getBean(JoinGroupService.class);
            List<Integer> integers = joinGroupService.queryGroupsByUserId(Integer.parseInt(id),
                    type.equals(MessageType.socketSys) ? MessageType.socketSysNum : MessageType.socketAppNum);
            integers.stream().forEach(integer -> {
                Set<Session> room = groupMap.get(integer.toString());
                room.remove(session);
                groupMap.put(integer.toString(), room);
                //判断是否删完了
                if (groupMap.get(integer.toString()).size() == 0) {
                    //删除这个
                    groupMap.remove(integer.toString());
                }
            });
        } catch (IOException e) {
            System.out.println("关闭连接出现错误");
        }
        //error.printStackTrace();
        System.out.println("用户：" + key + "异常下线\t" + MyDateUtil.simpleDateFormat.format(new Date()));
        System.out.println("当前在线人数：" + sessionMap.size());
        System.out.println(sessionMap.keySet() + "\n");
    }

    /**
     * @param message
     * @param session
     * @param id
     * @Author kdj
     * @Description 当收到消息会执行这个注解标记的方法。
     * @Date 2023/6/26 14:17
     * @Return void
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("id") String id, @PathParam("type") String type) {
        JSONObject data = JSONUtil.parseObj(message);
        String type1 = data.getStr("type");
        System.out.println(data + "aaaaa");
        try {
            switch (type1) {
                // 浏览器定时给服务器发送请求防止短线
                case "ping":
                    Message<String> response = new Message<>(200, "pong", "success");
                    this.sendMessage(JSONUtil.toJsonStr(response), session);
                    break;
                case "person_message": // 单聊
                    System.out.println("data::" + data);
                    handlePersonMessage(data, type);
                    break;
                case "check-person"://系统消息
                    handleCheckPerson(data, type);
                    break;
                case "recall": // 撤回消息
                    handleRecallMessage(data, type);
                    break;
                case "isRead": // 阅读消息
                    handleReadMessage(data, type);
                    break;
                case "del": // 删除消息
                    handleDelMessage(data, type);
                    break;
                case "add_friend": // 添加好友
                    handleAddFriend(data, type);
                    break;
                case "del_person"://删除好友
                    handleDelFriend(data, type);
                    break;
                case "del_noreason"://踢出群聊
                    handleDelnoreason(data, type, session);
                    break;
                case "storm_person"://禁言消息
                    handlestormFriend(data, type);
                    break;
                case "group_message"://群聊
                    handleGroupMessage(data, type);
                    break;
                case "create_group"://创建群聊
                    handleCreateGroup(data, type, session);
                    break;
                case "to_join_group"://去加入群聊
                    handleToJoinGroup(data, type, session);
                    break;
                case "prohibition"://被注销
                    prohibition(data);
            }
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
        }
    }

    /**
     * @param data
     * @Author kdj
     * @Description 被注销账号
     * @Date 2023/8/14 16:32
     * @Return void
     */
    private void prohibition(JSONObject data) {
        String toId = data.getStr("toUserId");
        //发送给管理员
        this.sendMessageById("app" + "_" + toId, data.toString());
    }


    /**
     * @param data
     * @param type
     * @param session
     * @Author kdj
     * @Description 去加入群聊
     * @Date 2023/7/27 19:46
     * @Return void
     */
    private void handleToJoinGroup(JSONObject data, String type, Session session) {
        //获取群聊id
        Integer groupId = data.getInt("groupId");
        //根据groupId给g管理员发消息
        GroupService bean = SpringContextUtil.getBean(GroupService.class);
        int userId = bean.getCreateUserId(groupId);
        //发送给管理员
        this.sendMessageById("sys" + "_" + userId, data.toString());
    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 创建群聊
     * @Date 2023/7/27 14:05
     * @Return void
     */
    private void handleCreateGroup(JSONObject data, String type, Session session) {
        String groupId = data.getStr("groupId");
        if (!groupMap.containsKey(groupId)) {
            // 创建房间不存在时，创建房间
            Set<Session> room = new HashSet<Session>();
            // 添加用户
            room.add(session);
            groupMap.put(groupId, room);
        }
    }


    /**
     * @Author 小江
     * @Description //TODO 踢出群聊
     * @Date 8:36 2023/8/2
     **/
    private void handleDelnoreason(JSONObject data, String type, Session session) {
        Integer fromId = data.getInt("fromId");
        String groupId = data.getStr("groupId");

        JoinGroupService bean = SpringContextUtil.getBean(JoinGroupService.class);
        boolean b = bean.delNoreason(fromId, groupId);

    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 接收群聊消息
     * @Date 2023/7/25 15:33
     * @Return void
     */
    private void handleGroupMessage(JSONObject data, String type) throws IOException {

        GroupMessageService groupMessageService = SpringContextUtil.getBean(GroupMessageService.class);
        String toId = data.getStr("toId");
        // 将data转为PersonMessage格式
        GroupMessageModel groupMessageModel = JSONUtil.toBean(data, GroupMessageModel.class);
        //添加到数据库
        int msgId = groupMessageService.insertGroupMsg(groupMessageModel);

        //发送给每个人
        for (Session session : groupMap.get(toId)) {//根据vaule获取key
            for (Object key : sessionMap.keySet()) {
                if (sessionMap.get(key).getId().equals(session.getId())) {
                    System.out.println("key" + key);
                    System.out.println("session:" + session.getBasicRemote());
                    session.getBasicRemote().sendText(data.toString());
                }
            }

        }

    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 删除好友
     * @Date 2023/7/21 14:57
     * @Return void
     */
    private void handleDelFriend(JSONObject data, String type) {
        //来自谁
        Integer fromId = data.getInt("fromId");
        Integer toId = data.getInt("toId");
        //删除好友
        FriendService bean = SpringContextUtil.getBean(FriendService.class);
        boolean b = bean.delFriend(fromId, toId);
        Message<String> response = null;
        if (b) {
            response = new Message<>(200, "del_person_status", "true");
            this.sendMessageById(type + "_" + toId, data.toString());
            this.sendMessageById(type + "_" + fromId, JSONUtil.toJsonStr(response));
        } else {
            response = new Message<>(200, "del_person_status", "false");
            this.sendMessageById(type + "_" + fromId, JSONUtil.toJsonStr(response));
        }

    }


    private void handlestormFriend(JSONObject data, String type) {
        //来自谁
        Integer fromId = data.getInt("fromId");
        Integer groupId = data.getInt("groupId");

        //禁言
        JoinGroupService bean = SpringContextUtil.getBean(JoinGroupService.class);

        Message<String> response = null;
    }


    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 添加好友
     * @Date 2023/7/20 20:41
     * @Return void
     */
    private void handleAddFriend(JSONObject data, String type) {
        String toId = data.getStr("toId");
        this.sendMessageById(type + "_" + toId, data.toString());
    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 删除消息
     * @Date 2023/7/20 14:25
     * @Return void
     */
    private void handleDelMessage(JSONObject data, String type) {
        MessageService messageService = SpringContextUtil.getBean(MessageService.class);
        int fromId=Integer.parseInt(data.getStr("fromId"));
        int userType = Integer.parseInt(data.getStr("userType"));
        //已读消息
        messageService.delMsg(data.getStr("uuid"),fromId,userType);
    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 将消息设置为已读
     * @Date 2023/7/19 17:23
     * @Return void
     */
    private void handleReadMessage(JSONObject data, String type) {

        MessageService messageService = SpringContextUtil.getBean(MessageService.class);
        //已读消息
        messageService.updateRead(data.getStr("uuid"), Integer.parseInt(data.getStr("readUserId")));
    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 单聊
     * @Date 2023/7/19 13:57
     * @Return void
     */
    private void handlePersonMessage(JSONObject data, String type) {

        PersonMessageService personMessageService = SpringContextUtil.getBean(PersonMessageService.class);
        String toId = data.getStr("toId");
        // 将data转为PersonMessage格式
        PersonMessageModel personMessageModel = JSONUtil.toBean(data, PersonMessageModel.class);
        int msgId = personMessageService.insertPerMsg(personMessageModel);
        this.sendMessageById(type + "_" + toId, data.toString());
    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description TODO 梁博文 系统消息
     * @Date 2023/7/19 13:58
     * @Return void
     */
    private void handleCheckPerson(JSONObject data, String type) {
        CheckService checkService = SpringContextUtil.getBean(CheckService.class);
        String toId = data.getStr("toUserId");
        CheckModel checkModel = JSONUtil.toBean(data, CheckModel.class);
        checkService.insertCheck(checkModel);
        this.sendMessageById("app" + "_" + toId, JSONUtil.toJsonStr(data));
    }

    /**
     * @param data
     * @param type
     * @Author kdj
     * @Description 撤回消息
     * @Date 2023/7/19 13:58
     * @Return void
     */
    private void handleRecallMessage(JSONObject data, String type) throws IOException {
        GroupMessageService groupMessageService = SpringContextUtil.getBean(GroupMessageService.class);
        //toid是群id   formid是当前登录者id  chattype    消息uuid
        PersonMessageService personMessageService = SpringContextUtil.getBean(PersonMessageService.class);
        String toId = data.getStr("toId");
        int chatType = data.getInt("chatType");
        if (chatType == MessageType.PerMsgType) {
            // 将接收到的消息转换成单聊类型
            PersonMessageModel personMessageModel = JSONUtil.toBean(data, PersonMessageModel.class);
            // 撤回
            personMessageService.recallMsg(personMessageModel);
            // 发送
            this.sendMessageById(type + "_" + toId, data.toString());
        } else if (chatType == MessageType.GroupMsgType) {
            GroupMessageModel groupMessageModel = JSONUtil.toBean(data, GroupMessageModel.class);
            //撤回消息
            groupMessageService.recallMsg(groupMessageModel);
            //发送给每个人
            for (Session session : groupMap.get(toId)) {
                System.out.println("session:" + session.getBasicRemote());
                session.getBasicRemote().sendText(data.toStringPretty());
            }

        }
    }
    //处理删除请求
//    private void lobbyMessage(String message,Session session){
//        redisOperator.cacheLobbyMessage(JSONUtil.parseObj(message).getStr("message"));
//        this.sendAllMessage(message);
//    }

    /**
     * @param id      类型+用户id
     * @param message
     * @Author kdj
     * @Description 根据session中的key发送消息
     * @Date 2023/6/30 15:41
     * @Return void
     */

    public void sendMessageById(String id, String message) {
        for (String key : sessionMap.keySet()) {
            // 遍历这个sessionMap里的id 如果sessionMap里没有接收人的id
            // 那么就不转发这条消息
            if (key.equals(id)) {
                this.sendMessage(message, sessionMap.get(key));
                return;
            }
        }
        //如果收信人不在线，则将信息进行缓存
    }

    /**
     * @param message
     * @param toSession
     * @Author kdj
     * @Description 根据session发送消息
     * @Date 2023/6/30 15:43
     * @Return void
     */
    public void sendMessage(String message, Session toSession) {
        try {
            if (toSession.isOpen()) {
                System.out.println("发送成功");
                toSession.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            System.out.println("发送失败");
            e.printStackTrace();
        }
    }

    // 将当前在线人数发送给每个浏览器（用户）
    private void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                if (session.isOpen()) {
                    session.getBasicRemote().sendText(message);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
